package erfgame.core.world.generator.terrain;


import erfgame.core.Point3D;
import erfgame.core.dao.ObjectDAO;
import erfgame.core.memory.Pointer;
import erfgame.core.world.terrain.TerrainConstants;
import erfgame.core.world.terrain.TerrainUtils;

public class LayeringTerrainBlockGeneratorProxy implements TerrainBlockGenerator {
	
	private int layerDepth;
	private byte layerType;
	private TerrainBlockGenerator proxied;
	private ObjectDAO<Point3D, Pointer<byte[][][]>> blockTerrainDAO;
	private int maxDepth;
	
	public LayeringTerrainBlockGeneratorProxy( 
			TerrainBlockGenerator proxied, 
			ObjectDAO<Point3D, Pointer<byte[][][]>> blockTerrainDAO, 
			int layerDepth, 
			byte layerType, 
			int maxDepth 
	) {
		this.proxied = proxied;
		this.blockTerrainDAO = blockTerrainDAO;
		this.layerDepth = layerDepth;
		this.layerType = layerType;
		this.maxDepth = maxDepth;
	}

	public boolean generate(int bx, int by, int bz, int bw, int bh, int bd,
			byte[][][] terrain) {
		boolean result = this.proxied.generate( bx, by, bz, bw, bh, bd, terrain );
		Pointer<byte[][][]> terrainBelow = null;
		for( int x=bw; x>0; ) {
			x--;
			for( int y=bh; y>0; ) {
				y--;
				int depth = TerrainUtils.getSurfaceDepth( terrain, x, y );
				if( depth < 0  && this.maxDepth > bz + 1 ) {
					if( terrainBelow == null ) {
						terrainBelow = this.blockTerrainDAO.retrieveObject( new Point3D( bx, by, bz+1 ) );
					}
					depth = this.getSurfaceDepth(terrainBelow.getValue(), x, y);
					if( depth >= 0 ) {
						depth += bd;
					}
				}
				if( depth > 0 ) {
					result = true;
					for( int z = Math.max( 0, depth - this.layerDepth ); z<Math.min( depth, bd ); z++ ) {
						terrain[x][y][z] = this.layerType;
					}
				}
			}
		}
		return result;
	}

	private int getSurfaceDepth( byte[][][] terrain, int x, int y ) {
		byte[] terrainXY = terrain[x][y];
		for( int z = 0; z<terrainXY.length; z++ ) {
			byte ttz = terrainXY[z];
			if( ttz != TerrainConstants.TERRAIN_TYPE_AIR && ttz != this.layerType ) {
				return z;
			}
		}
		return -1;
		
	}
}
